/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "nril.h"
#include "rilConfig.h"
/* Public typedef ------------------------------------------------------------*/
struct MN316Socket
{
	uint8_t			id;
	bool 			isUsed;
	struct Socket 	*parent;
};

/* Pravite variables ----------------------------------------------------------*/
extern struct Socket *GetSocketById(int id);

int MN316_StartUp(struct RilDevice *device);
int MN316_ShutDown(struct RilDevice *device);
int MN316_Init(struct RilDevice *device);
int MN316_Reset(struct RilDevice *device, bool usePin);
int MN316_Pdp(struct RilDevice *device);

int MN316_Release(struct RilDevice *device, struct Socket *socket);
int MN316_Connect(struct RilDevice *device, struct Socket *socket, const struct sockaddr *name, socklen_t namelen);
int MN316_Disconnect(struct RilDevice *device, struct Socket *socket);
int MN316_Send(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags);
int MN316_Recv(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags);
int MN316_SendTo(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen);
int MN316_RecvFrom(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags, struct sockaddr *from, socklen_t *fromlen);

int MN316_Ignore(char *data);
int MN316_Sim(char *data);
int MN316_Signal(char *data);
int MN316_Imei(char *data);
int MN316_Imsi(char *data);
int MN316_PdpInfo(char *data);
int MN316_Cclk(char *data);
int MN316_SocketCon(char *data);
int MN316_SocketRx(char *data);
int MN316_GnssInfo(char *data);
int MN316_GnssType(char *data);

const struct NatUrc mn316Urcs[] =  {
        {"+CPIN",               MN316_Sim },
        {"+CSQ",                MN316_Signal },
        {"+CGSN:",            	MN316_Imei },
        {"+CCLK",               MN316_Cclk },
        {"CLOSED",              MN316_SocketCon},
        {"+NSORF",             	MN316_SocketRx},
		{"+ASSERT",            	MN316_Ignore},
        NULL
};
	
const struct Nat mn316Nat = {.urcs = mn316Urcs};

const struct RilOperations mn316Ops = {
    .startUp = MN316_StartUp,
    .shutDown = MN316_ShutDown,
    .init = MN316_Init,
    .reset = MN316_Reset,
    .pdp = MN316_Pdp,
    .opsSocket = {
		.release = MN316_Release,
        .connect = MN316_Connect,
        .disconnect = MN316_Disconnect,
        .send = MN316_Send,
        .recv = MN316_Recv,
		.sendto = MN316_SendTo,
		.recvfrom = MN316_RecvFrom
    }
};

struct RilDeviceConfig mn316Config = {0};
struct RilDeviceStatus mn316Status = {0};

struct MN316Socket socketsMN316[2] = {0};


#ifdef USING_OS
RilSem semMN316AT;
#endif

/* Public define -------------------------------------------------------------*/
#define SOCKET_ID(socket)	(((struct MN316Socket *)socket->device)->id)
#define MN316_TCP_MTU	700

/* Public macro --------------------------------------------------------------*/
/* Public function prototypes ------------------------------------------------*/
extern uint32_t NEDelay_Ms(int n);
extern void NETimestamp_Set(double n);
extern struct Socket* Ril_GetSocketById(int id);
/********************************************************************************************* 

	MN316 opertions

  ********************************************************************************************/
/************************************
start up, set startup pin high 2s at least
  ***********************************/
int MN316_StartUp(struct RilDevice *device)
{	
	device->adapter->pinCtrl(RIL_PIN_STARTUP, 1);
	NEDelay_Ms(3000);
	device->adapter->pinCtrl(RIL_PIN_STARTUP, 0);
	return 0;	
}
/************************************
shut down, set startup pin low 3.5s at least
  ***********************************/
int MN316_ShutDown(struct RilDevice *device)
{	return 0;	}
/************************************
	MN316 Init
  ***********************************/
int MN316_Init(struct RilDevice *device)
{
	int result = 0;
	const char *initCmds[] = {
		"AT+CGMR\r\n",
		"AT+CFUN?\r\n",
		"AT+CFUN=1\r\n",
		"AT+CIMI\r\n",
		"AT+CGSN=1\r\n",
		NULL,   
	};
	char **cmd = (char **)initCmds;
	
#ifdef USING_OS
	RIL_SEM_INIT(&semMN316AT, MN316AT, 1);
#endif
	
	// loop cmd list
	do
	{
		RilDevice_ClearMessages();
		result = RilDevice_ExecuteCommand(device, 3000, "OK", *cmd);
		
		/* get nothing*/
		if(result < 0)
		{	return -2;	}
		
		/* get non OK response */
		if(Nat_ResponseCheck(&(device->adapter->response), "OK") == NULL)
		{	continue;	}//{	return -1;	}
		
		cmd += 1;
	}while(*cmd != NULL);
	
	return 0;
}
/************************************
	MN316 Reset
  ***********************************/
int MN316_Reset(struct RilDevice *device, bool usePin)
{
	memset(socketsMN316, 0 ,sizeof(socketsMN316));
	return 0;
}
/************************************
	MN316 Pdp
  ***********************************/
int MN316_Pdp(struct RilDevice *device)
{
	int result = 0, countRetry = 0;
	
	const char *initCmds[] = {
		"AT+CGATT=1\r\n",
		"AT+CEDRXS=0,5\r\n",
		"AT+CSQ\r\n",
		"AT+NUESTATS=CELL\r\n",
		NULL,   
	};
	char **cmd = (char **)initCmds;
	
#ifdef USING_OS
	RIL_SEM_INIT(&semMN316AT, MN316AT, 1);
#endif
	
	// loop cmd list
	do
	{
		RilDevice_ClearMessages();
		result = RilDevice_ExecuteCommand(device, 3000, "OK", *cmd);
		
		/* get nothing*/
		if(result < 0)
		{	return -2;	}
		
		/* get non OK response */
		if(Nat_ResponseCheck(&(device->adapter->response), "OK") == NULL)
		{	continue;	}//{	return -1;	}
		
		cmd += 1;
	}while(*cmd != NULL);
	
	do
	{
		if((result = RilDevice_ExecuteCommand(device, 3000, "+CGATT:1", "AT+CGATT?\r\n")) == 0)
		{	break;	}
		NEDelay_Ms(1000);
		
		countRetry ++;
		if(countRetry > 20)
		{	return -102;	}
	}while(1);
	
	result = RilDevice_ExecuteCommand(device, 1000, "+CCLK", "AT+CCLK?\r\n");
	NEDelay_Ms(1000);
	
	return 0;
}
/************************************
	MN316_Dns
  ***********************************/
int MN316_Dns(struct RilDevice *device, char *host, char *ip)
{
	char *ipString = NULL;
	int i = 0, result;
	
	/* domain name resolution */
	for(i=0; i<strlen(host); i++)
	{
		if(host[i] != '.' & host[i] > '9')
		{	break;	}
	}
	
	if(i == strlen(host))
	{	ipString = host;	}
	else
	{
		RilDevice_ExecuteCommand(device, 10000, "+CMDNS", "AT+CMDNS=\"%s\"\r\n", host);
		if((ipString = Nat_ResponseCheck(&(device->adapter->response), "+CMDNS")) == NULL)
		{	return  -6;	}			// ENXIO
		else
		{	ipString += 7;	}
	}
	
	memcpy(ip, ipString, strlen(ipString));
	
	return 0;
}
/************************************************************************************************************
	MN316 socket sal api
  ***********************************************************************************************************/
/************************************
	MN316_Socket , mn316 will allocate a socket id when send NSOCR command
  ***********************************/
int MN316_Socket(struct RilDevice *device, struct Socket *socket)
{
	int result = 0;
	char *response;
	
#ifdef USING_OS
	RIL_SEM_TAKE(&semMN316AT, -1);
#endif	
	if(socket->device == NULL || ((struct MN316Socket *)socket->device)->isUsed == false)
	{
		result = RilDevice_ExecuteCommand(device, 0, 0, NULL);
		result = RilDevice_ExecuteCommand(device, 3000, "+NSOCR", "AT+NSOCR=\"%s\",%d,0,2\r\n", 
																   (socket->type == SOCK_STREAM)? "STREAM":"DGRAM",
																   (socket->type == SOCK_STREAM)? 6:17);
		
		/* receive error */
		if(result != 0)
		{	goto __end;	}
		
		/* receive the socket id by device alloc */
		response = Nat_ResponseCheck(&(device->adapter->response), "+NSOCR");
		if(response == NULL)
		{	
			result = -8;			// ENOEXEC
			goto __end;
		}

		sscanf(response, "+NSOCR:%d", &result);
		socketsMN316[result].id = result;
		socketsMN316[result].isUsed = true;
		socketsMN316[result].parent = socket;
		socket->device = socketsMN316 + result;
	}
	
__end:
	
#ifdef USING_OS
	RIL_SEM_RELEASE(&semMN316AT);
#endif
	
	return result;
}

/************************************
	MN316 Socket release
  ***********************************/
int MN316_Release(struct RilDevice *device, struct Socket *socket)
{
	memset(socketsMN316 + SOCKET_ID(socket), 0, sizeof(struct MN316Socket));
}

/************************************
	MN316 Socket close socket
  ***********************************/
int MN316_CloseSocket(struct RilDevice *device, struct Socket *socket)
{
	int result = 0, id = 0;
	char *response;

#ifdef USING_OS
	RIL_SEM_TAKE(&semMN316AT, -1);
#endif
	
	if(socket->device == NULL)
	{	
		result = -6;			// ENXIO
		goto __end;
	}
	
	RilDevice_ExecuteCommand(device, 3000, "+NSOCLI", "AT+NSOCL=%d\r\n", SOCKET_ID(socket));
	
	if(Nat_ResponseCheck(&(device->adapter->response), "OK") == NULL)
	{	
		result = -8;			// ENOEXEC
		goto __end;
	}
	
	if((response = Nat_ResponseCheck(&(device->adapter->response), "NSOCLI")) != NULL)
	{
		sscanf(response, "+NSOCLI:%d", &id);
		memset(socketsMN316 + id, 0, sizeof(struct MN316Socket));			// clear all private socket info
		
		result = 0;	
		goto __end;
	}

#ifdef USING_OS
	RIL_SEM_RELEASE(&semMN316AT);
#endif
__end:
	socket->device = NULL;
	return result;
}

/************************************
	MN316 Socket Connect
  ***********************************/
int MN316_Connect(struct RilDevice *device, struct Socket *socket, const struct sockaddr *name, socklen_t namelen)
{
	union SocketAddress *address = (union SocketAddress *)name;
	int result = 0;
	char* response = NULL, hostIP[17] = {0};

#ifdef USING_OS
	RIL_SEM_TAKE(&semMN316AT, -1);
#endif
	result = MN316_Socket(device, socket);
	if(result < 0)
	{	goto __end;	}
	
	result = MN316_Dns(device, address->addr_at.sat_addr, hostIP);
	if(result < 0)
	{	goto __end;	}
	
	result = RilDevice_ExecuteCommand(device, 10000, "CONNECT", "AT+NSOCO=%d,%s,%d\r\n", 
														  SOCKET_ID(socket),
														  hostIP,
														  address->addr_at.sat_port);
		

	if(Nat_ResponseCheck(&(device->adapter->response), "CONNECT OK") != NULL
		|| Nat_ResponseCheck(&(device->adapter->response), "8002") != NULL)
	{	result =  0;	}
	else if(Nat_ResponseCheck(&(device->adapter->response), "CONNECT FAIL") != NULL)
	{	result = -111;	}
	
__end:
	
#ifdef USING_OS
	RIL_SEM_RELEASE(&semMN316AT);
#endif
	
	return result;
}
/************************************
	MN316 Socket Disconnect
  ***********************************/
int MN316_Disconnect(struct RilDevice *device, struct Socket *socket)
{
	int result = 0;

#ifdef USING_OS
	RIL_SEM_TAKE(&semMN316AT, -1);
#endif
	
	result = MN316_Socket(device, socket);
	if(result < 0)
	{	return result;	}
	
	RilDevice_ExecuteCommand(device, 3000, "+NSOCLI", "AT+NSOCL=%d\r\n", SOCKET_ID(socket));
	
	if(Nat_ResponseCheck(&(device->adapter->response), "OK") == NULL)
	{	
		result = -8;			// ENOEXEC
		goto __end;
	}
	
	if(Nat_ResponseCheck(&(device->adapter->response), "NSOCLI") != NULL)
	{
		result = 0;	
		goto __end;
	}

#ifdef USING_OS
	RIL_SEM_RELEASE(&semMN316AT);
#endif
__end:
	return result;
}
/************************************
	MN316 Socket Send
  ***********************************/
int MN316_Send(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags)
{
	int result = 0;
	int seek = 0, count, i, countSend = 0;
	char *packet = NULL, *response;
	
#ifdef USING_OS
	RIL_SEM_TAKE(&semMN316AT, -1);
#endif
	result = MN316_Socket(device, socket);
	if(result < 0)
	{	return result;	}
	
	/* alloc memory to store entire command */
	count = ((size > MN316_TCP_MTU)? MN316_TCP_MTU:size) * 2 + 32;
	packet = (char *)malloc(count);
	if(packet == NULL)
	{	
		result =  -105;
		goto __end;
	}
	memset(packet, 0, count);
	
	while(seek < size)
	{
		/* batch */
		countSend = (((size - seek) > MN316_TCP_MTU)? MN316_TCP_MTU:(size-seek));
		
		/* send packet */
		sprintf(packet,"AT+NSOSD=%d,%d,", SOCKET_ID(socket), countSend);
		count = strlen(packet);
		for(i=0; i<countSend; i++)
		{	
			sprintf(packet + count, "%02X", *((uint8_t *)data + i + seek));
			count += 2;
		}
		memcpy(packet + count, "\r\n", 2);
		count += 2;
		
		/* send packet and rx  */
		if(RilDevice_SendRaw(device, 5000, "+NSOSD", packet, count) != 0)
		{	
			result = -6;			// ENXIO
			goto __end;	
		}
		seek += countSend;
	}
	
__end:
#ifdef USING_OS
	RIL_SEM_RELEASE(&semMN316AT);
#endif
	if(packet != NULL)
	{	free(packet);	}
	return result;
}
/************************************
	MN316 Socket Receive
  ***********************************/
int MN316_Recv(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags)
{
	int count = 0;
	struct SocketStream *tcpObj = (struct SocketStream *)(socket->obj);
	RilDevice_ExecuteCommand(device, 0, 0, NULL);	
	
	if(tcpObj->seek == 0)
	{	return 0;	}
	
	/* get data from buffer rx */
	if((tcpObj->seek + 1) > size)
	{	
		count = size;
		memcpy(mem, tcpObj->buffer, count);
		memcpy(tcpObj->buffer, tcpObj->buffer + count - 1, tcpObj->seek + 1 - count);
		tcpObj->seek = tcpObj->seek + 1 - count;
	}
	else
	{
		count = tcpObj->seek + 1;
		memcpy(mem, tcpObj->buffer, count);
		tcpObj->seek = 0;
	}
	
	return count;
}
/************************************
	MN316 Socket Sendto
  ***********************************/
int MN316_SendTo(struct RilDevice *device, struct Socket *socket, void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen)
{
	union SocketAddress *address = (union SocketAddress *)to;
	int result = 0, count = 0, i;
	char *packet = NULL, *response, hostIP[17] = {0};

#ifdef USING_OS
	RIL_SEM_TAKE(&semMN316AT, -1);
#endif

	result = MN316_Socket(device, socket);
	if(result < 0)
	{	goto __end;	}
	
	
	/* alloc memory to store entire command */
	count = size * 2 + 32;
	packet = (char *)malloc(count);
	if(packet == NULL)
	{	
		result =  -105;
		goto __end;
	}
	memset(packet, 0, count);

	/* domain name resolution */
	result = MN316_Dns(device, address->addr_at.sat_addr, hostIP);
	if(result < 0)
	{	goto __end;	}
	
	/* combine at command , raw data, LF  */
	sprintf(packet,"AT+NSOST=%d,%s,%d,%d,", SOCKET_ID(socket), 
											hostIP,
											address->addr_at.sat_port,
											size);
	count = strlen(packet);
	for(i=0; i<size; i++)
	{	
		sprintf(packet + count, "%02X", *((uint8_t *)data + i));
		count += 2;
	}
	memcpy(packet + count, "\r\n", 2);
	count += 2;
	
	
	/* wait for send status */
	RilDevice_SendRaw(device, 0, 0, packet, count);
__end:
#ifdef USING_OS
	RIL_SEM_RELEASE(&semMN316AT);
#endif
	if(packet != NULL)
	{	free(packet);	}

	return tolen;
}
/************************************
	MN316 Socket Receive from
  ***********************************/
int MN316_RecvFrom(struct RilDevice *device, struct Socket *socket, void *mem, size_t size, int flags, struct sockaddr *from, socklen_t *fromlen)
{
	int countRecv = 0;
	struct SocketDgRam *udpObj = (struct SocketDgRam *)socket->obj;
	
	RilDevice_ExecuteCommand(device, 0, 0, NULL);

	for(int i=0; i<RIL_SOCKET_UDP_LIST_COUNT; i++)
	{
		if(udpObj->msgList[i].start != NULL)
		{
			countRecv = ((udpObj->msgList[i].size > size)? size: udpObj->msgList[i].size);
			memcpy(mem, udpObj->msgList[i].start, countRecv);
			
			if(from != NULL)
			{
				memcpy(from, udpObj->msgList[i].start, sizeof(struct sockaddr));
				*fromlen =  (socklen_t)sizeof(struct sockaddr_at);
			}
			free(udpObj->msgList[i].start);
			udpObj->msgList[i].start = NULL;
			udpObj->msgList[i].size = 0;
			
			break;
		}
	}
	
	return countRecv;
}

/*********************************************************************************************/

//  urc functions

/*********************************************************************************************/
int MN316_Ignore(char *data)           // "+CPIN: READY"
{   
    if(strncmp("+ASSERT", data, 6) == 0)
	{	return	6;	}
    return 10;
}
/************************************
	MN316 Sim status
  ***********************************/
int MN316_Sim(char *data)           // "+CPIN: READY"
{   
    if(strstr((char*)data, "READY") != NULL)
    {   mn316Status.simStatus = 1;    }
    else
    {   mn316Status.simStatus = -1;   }
    return 10;
}
/************************************
	MN316 signal
  ***********************************/
int MN316_Signal(char *data)
{   
    int temp32 = 0;
    sscanf(data, "+CSQ: %d,", &temp32);
    mn316Status.csq = temp32;
    return 10;
}

/************************************
	MN316 IMEI
  ***********************************/
int MN316_Imei(char *data)
{
    sscanf(data, "+CGSN: %s", mn316Status.imei);
    
    return 5 + strlen(mn316Status.imei);
}
/************************************
	MN316 CCLK
  ***********************************/
int MN316_Cclk(char *data)
{
	struct tm calendar;
	time_t timestamp = 0;
	
	sscanf(data, "+CCLK:%d/%d/%d, %d:%d:%d",
				 &calendar.tm_year,
				 &calendar.tm_mon,
				 &calendar.tm_mday,
				 &calendar.tm_hour,
				 &calendar.tm_min,
			     &calendar.tm_sec);
	calendar.tm_mon -= 1;
	calendar.tm_year += 2000 - 1900;
	
	timestamp = mktime(&calendar);
	NETimestamp_Set(timestamp);
    return 26;
}
/************************************
	MN316 tx status
  ***********************************/
int MN316_SocketCon(char *data)
{
	int socketId = 0;
	
	sscanf(data, "%d,CLOSED", &socketId);
	
	return 8;
}

/************************************
	MN316 rx urc
  ***********************************/
extern void HexStringToByteArray(char *src, char *dst, size_t length);
int MN316_SocketRx(char *data)
{
	int countRecv = 0, socketId = 0, countRemain = 0;
    char *start=NULL, *packet=NULL;
    struct Socket *socket = NULL;
	struct SocketStream *tcpObj = NULL;
	struct SocketDgRam *udpObj = NULL;

	sscanf(data, "+NSORF:%d,", &socketId);
	packet = strstr(data, ",");
	packet += 1;							
	packet = strstr(packet, ",");				// jump ip
	packet += 1;
	packet = strstr(packet, ",");				// jump port
	packet += 1;
	sscanf(packet, "%d,%s", &countRecv, start);
	packet = strstr(packet, ",");				// jump port
	start = packet + 1;
	
	if(strlen(start) >= countRecv * 2)
	{	HexStringToByteArray(start, start, strlen(start));	}
	
	socket = socketsMN316[socketId].parent;
	
	/* put recv packet into socket rx buffer */
	if(socket != NULL)
	{
		switch(socket->type)
		{
			case SOCK_STREAM:
				tcpObj = (struct SocketStream *)(socket->obj);
				countRemain = RIL_SOCKET_TCP_RX_BUFFER_SIZE - tcpObj->seek - 1;
				if(countRemain > countRecv)
				{	
					memcpy(tcpObj->buffer + tcpObj->seek, start, countRecv);
					tcpObj->seek += countRecv;
				}
				else
				{
					memcpy(tcpObj->buffer + tcpObj->seek, start, countRemain);
					tcpObj->seek = RIL_SOCKET_TCP_RX_BUFFER_SIZE - 1;
				}
				break;
			case SOCK_DGRAM:
				udpObj = (struct SocketDgRam *)(socket->obj);
				for(int i=0; i<RIL_SOCKET_UDP_LIST_COUNT; i++)
				{
					if(udpObj->msgList[i].start == NULL
						|| udpObj->msgList[i].size == 0)
					{
						
						udpObj->msgList[i].start = malloc(countRecv);
						if(udpObj->msgList[i].start == NULL)
						{	break;	}
						
						memcpy(udpObj->msgList[i].start, start, countRecv);
						udpObj->msgList[i].size = countRecv;
						
						break;
					}
				}
		}
	}
	return (uint32_t)start - (uint32_t)data + countRecv;
}


RIL_MODULE_INSTALL(mn316)
